# -*- coding: utf-8 -*-

"""
Module implementing CDriveTools.
"""
import time
import serial, re

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

from Ui_DriveTools import Ui_MainWindow
from CScope import *
from CKeypad import *
from CGDKeypad import *
from ModbusRTU import *
#from GuardCommDevice import CGuardCommDevice
from ScopeComm import *
from CDCMP import CDCMP
from Crc import Crc32
from About import CAbout
import serial.tools.list_ports
from guiqwt.plot import CurveDialog

class CDriveTools(QMainWindow, Ui_MainWindow):
    
    FAS = [\
        "FAS_OK_EOF", \
        "FAS_OK", \
        "FAS_OK_NOT_EXIST", \
        "FAS_OK_ALREADY_OPEN", \
        "FAS_OK_NOT_OPENED", \
        "FAS_OK_NOT_SUPPORT", \
        "FAS_OK_CRC_ERROR", \
        "FAS_OK_LENGTH_ERROR", \
        "FAS_OK_ACTION_FAILED", \
        "FAS_OK_TOO_MUCH_OPEN", \
        "FAS_OK_DATA_ERROR", \
        "FAS_VERSION_ERROR", \
        "FAS_TYPE_ERROR", \
        "FAS_TOO_MUCH_DATA"\
    ]
    
    
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None):
        """
        Constructor

        @param parent reference to the parent widget
        @type QWidget
        """
        super(CDriveTools, self).__init__(parent)
        self.setupUi(self)

        self.pt = re.compile('^(1 +|abs +)[\dA-Fa-f]{8} +\w+')
        #self.ptAddr = re.compile(' +0+[\dA-Fa-f]+')
        #self.ptName = re.compile(' +\w+')
        self.ptMember = re.compile('_\w+__')
        self.ptClass = re.compile('__\d+\w+')

        self.ptHex = re.compile('0x[\dA-Fa-f]+')
        self.ptFuncode = re.compile('F\d+.\d+')
        
        
        rbScpChList = [self.rbScpCh1, self.rbScpCh2, self.rbScpCh3, self.rbScpCh4, self.rbScpCh5, self.rbScpCh6, self.rbScpCh7, self.rbScpCh8]
        self.bgChGroup = QButtonGroup()        
        for ch in range(0, len(rbScpChList)):
            self.bgChGroup.addButton(rbScpChList[ch], ch)
            rbScpChList[ch].clicked.connect(self.on_ChRadio_Clicked)
        self.rbScpCh1.setChecked(True)

        self.rbScpCh7.setEnabled(False)
        self.rbScpCh8.setEnabled(False)

        self.UpdateCommPortList()
        
        self.stopBitList = [serial.STOPBITS_ONE,  serial.STOPBITS_TWO]
        self.parityList = [serial.PARITY_NONE, serial.PARITY_ODD,  serial.PARITY_EVEN] 
            
        self.cbKpParity.addItems(self.parityList)
        self.cbUsrParity.addItems(self.parityList)
        
        for sb in self.stopBitList:
            self.cbKpStopBit.addItem(str(sb), sb)
            self.cbUsrStopBit.addItem(str(sb), sb)
            
        self.scopeChannelList = []
    
        for i in range(0, 8):
            self.scopeChannelList.append(CScopeChannel())
            
        self.cbScpIsParam.setEnabled(False)

        
        scopeDisplayWidget = CurveDialog(edit = False,  toolbar = True, wintitle = 'Scope', parent = self, 
                                options = dict(xlabel = 'time/sample',  ylabel = 'value'))
        
        scopeDisplayWidget.setMinimumSize(10, 10)
        self.curveLayout.addWidget(scopeDisplayWidget)
        self.scopeDisplay = CScopeDisplay(scopeDisplayWidget, self)



        self.keypadDisplay = CKeypadDisplay(self.lbKpParamInfo, self.leKpDisplay, self.teParamDescription)

        self.deviceCtlWidgetList = [self.btDCRead, self.btDCWrite, self.btMDDump, \
                                        self.btPDDumpAll, self.btPDDumpND, self.btPDLoad, \
                                        self.btUploadFW, self.btDownloadFW, self.btReset, \
                                        self.actionSetToKeypad, self.actionSetToScope]
                                        
        self.keypadWidgetList = [self.btKpEsc, self.btKpEnter, self.btKpLeft,self.btKpRight, \
                                    self.btKpUp, self.btKpDown, self.btKpRun, self.btKpStop]

        for widget in self.keypadWidgetList:
            widget.setEnabled(False)

        for widget in self.deviceCtlWidgetList:
            widget.setEnabled(False)
            
        cmp = self.cbDCAddr.completer()
        cmp.setCompletionMode(QCompleter.PopupCompletion)
        cmp.setCaseSensitivity(Qt.CaseInsensitive)
        cmp.setFilterMode(Qt.MatchContains)  
        
        cmp = self.cbScpName.completer()
        cmp.setCompletionMode(QCompleter.PopupCompletion)
        cmp.setCaseSensitivity(Qt.CaseInsensitive)
        cmp.setFilterMode(Qt.MatchContains)  
        

    def UpdateCommPortList(self):
        
        # Get port list and add to combo box
        portList = list(serial.tools.list_ports.comports())
        
        self.cbKpPort.clear()
        self.cbUsrPort.clear()
        
        for port in portList:
            self.cbKpPort.addItem(port[0])
            self.cbUsrPort.addItem(port[0])


    def GetKeypadPortObject(self):
                
        keypadPort = serial.Serial(port = self.cbKpPort.currentText(),\
                                            baudrate = int(self.cbKpBaud.currentText()), \
                                            stopbits = self.stopBitList[self.cbKpStopBit.currentIndex()], \
                                            parity = self.parityList[self.cbKpParity.currentIndex()], 
                                            timeout = self.sbKpTimeout.value())
                                            
        return keypadPort

    def GetUserPortObject(self):
                
        UserPort = serial.Serial(port = self.cbUsrPort.currentText(),\
                                            baudrate = int(self.cbUsrBaud.currentText()), \
                                            stopbits = self.stopBitList[self.cbUsrStopBit.currentIndex()], \
                                            parity = self.parityList[self.cbUsrParity.currentIndex()], 
                                            timeout = self.sbUsrTimeout.value())
                                            
        return UserPort


    def on_Keypad_Timeout(self):
        
        try:
            self.keypad.UpdateDisplay()
        except Exception as e:
            QMessageBox.critical(
                self,
                self.tr("Error with comm"),
                self.tr(str(e)),
                QMessageBox.StandardButtons(
                    QMessageBox.Ok),
                QMessageBox.Ok)
                
            self.btKpConnect.setChecked(False)

    def ParseMapRecord(self, record):
        
        #Remove multiple spaces
        ms = re.compile('\s+')
        record = ms.sub(' ', record)
        
        record = record.split(' ')
        addr = '0x' + record[1]
        name = record[2]

        #match = self.ptAddr.search(record)
        #addr = '0x' + match.group()

        #match = self.ptName.search(record)
        #name = match.group()

        match = self.ptClass.search(name)
        if match:

            cls = match.group()

            if cls[3] >= '0' and cls[3] <= '9':
                cls = cls[4:len(cls)]
            else:
                cls = cls[3:len(cls)]

            match = self.ptMember.search(name)
            member = match.group()
            member = member[1:len(member) - 2]

            varName = member + '::' + cls

        else:
            if name[0] == '_':
                varName = name[1:len(name)]
            else:
                varName = name

        return varName + ' ' + addr


    def on_ChRadio_Clicked(self):

        ch = self.bgChGroup.checkedId()


        self.sbScpSlaveAddr.setValue(self.scopeChannelList[ch].slaveAddr)
        self.leScpAddr.setText(hex(self.scopeChannelList[ch].addr))

        idx = self.cbScpName.findText(self.scopeChannelList[ch].name)
        
        if idx != -1:
            self.cbScpName.setCurrentIndex(idx)
        else:
            self.cbScpName.setCurrentText(self.scopeChannelList[ch].name)            
        
        self.cbScpSize.setCurrentIndex(self.scopeChannelList[ch].size)        
        self.cbScpSign.setCurrentIndex(self.cbScpSign.findText(self.scopeChannelList[ch].type))

        self.leScpScale.setText(str(self.scopeChannelList[ch].scale))
        self.leScpOffset.setText(str(self.scopeChannelList[ch].offset))
        
        self.cbScpActive.setChecked(self.scopeChannelList[ch].active)
        self.cbScpIsParam.setChecked(self.scopeChannelList[ch].isParam)


    @pyqtSlot()
    def on_actionLoad_Map_triggered(self):
        """
        Slot documentation goes here.
        """
        file, filter = QFileDialog.getOpenFileName(
        self,
        'Select map file',
        '',
        "Map file(*.map)",
        None,
        QFileDialog.DontUseNativeDialog)


        if file != None and file != '':

            self.statusBar.showMessage(file)             #在状态栏显示文件地址
            #Clear items first
            self.cbScpName.clear()
            self.cbScpName.addItem('')
            self.cbDCAddr.clear()
            self.cbDCAddr.addItem('')


            for line in open(file):

                match = self.pt.match(line)
                if match:
                    item = self.ParseMapRecord(line)

                    # Add to combo
                    self.cbScpName.addItem(item)
                    self.cbDCAddr.addItem(item)



    @pyqtSlot(bool)
    def on_btScpConnect_toggled(self, checked):
        """
        Slot documentation goes here.

        @param checked DESCRIPTION
        @type bool
        """
        
        if checked:
            
            try:     
                
                if self.cbScopePort.currentIndex() == 0:
                    self.scopePort = self.GetKeypadPortObject()
                else:
                    self.scopePort = self.GetUserPortObject()
                    
                if self.rbScopeSlow.isChecked():
                    self.scopeModbus = CModbus(self.scopePort)
                    self.scopeModbus.SetDeviceAddr(self.spScpAddress.value())
                    self.scopeProtocol = CSlowScopeComm(self.scopeModbus,  self.scopeChannelList, None)
                    mode = 'S'
                else:
                    self.scopeProtocol = CFastScopeComm(self.scopePort,  self.scopeChannelList, None)
                    mode = 'F'
                    
                self.scope = CScope(self.scopeDisplay, self.scopeChannelList, self.scopeProtocol, mode)
                    

                self.scopePort.close()
                self.scopePort.open()
                self.scopePort.flushInput()
                
            except Exception as e:
                QMessageBox.critical(self,"Faild to open port", str(e))
                self.btScpConnect.setChecked(False)
                return

                
            self.cbScopePort.setEnabled(False)
            self.rbScopeSlow.setEnabled(False)
            self.rbScopeFast.setEnabled(False)
            self.btScpApply.setEnabled(False)
            self.spScpAddress.setEnabled(False)
            self.btScpHold.setEnabled(True)
            self.btScpHold.setChecked(False)
            
            self.scope.Start()
                
                
        else:
            
            self.cbScopePort.setEnabled(True)
            self.rbScopeSlow.setEnabled(True)
            self.rbScopeFast.setEnabled(True)
            self.btScpApply.setEnabled(True)
            self.spScpAddress.setEnabled(True)
            self.btScpHold.setEnabled(False)
            self.btScpHold.setChecked(False)   
            
            
            try:
                #28.0: Start/Stop scope
                self.DCModBus.WriteSingle(28 << 8, 0)
            except:
                pass
            
            
            try:
                
                self.scope.Stop()
                time.sleep(0.1)
            except Exception as e:
                self.statusBar.showMessage(str(e))
            
            self.scopePort.close()
                
            try:
                
                del self.scopePort
                del self.scope
                del self.scopeProtocol
                del self.scopeModbus
                
            except:
                pass



    @pyqtSlot(bool)
    def on_btScpHold_toggled(self, checked):
        """
        Slot documentation goes here.

        @param checked DESCRIPTION
        @type bool
        """
        
        self.scope.Hold(checked)
        
        #if checked == True:
        #    self.scope.Finalize()


    @pyqtSlot()
    def on_btKpLeft_clicked(self):
        """
        Slot documentation goes here.
        """
        self.keypad.LeftAction()

    @pyqtSlot()
    def on_btKpEnter_clicked(self):
        """
        Slot documentation goes here.
        """
        self.keypad.EnterAction()

    @pyqtSlot()
    def on_btKpEsc_clicked(self):
        """
        Slot documentation goes here.
        """
        self.keypad.EscAction()

    @pyqtSlot()
    def on_btKpUp_clicked(self):
        """
        Slot documentation goes here.
        """
        self.keypad.UpAction()

    @pyqtSlot()
    def on_btKpRight_clicked(self):
        """
        Slot documentation goes here.
        """
        self.keypad.RightAction()

    @pyqtSlot()
    def on_btKpStop_clicked(self):
        """
        Slot documentation goes here.
        """
        stopMode = 0
        if self.rbKpSMCoast.isChecked():
            stopMode = 1
        self.keypad.StopAction(stopMode)

    @pyqtSlot(bool)
    def on_btKpConnect_toggled(self, checked):
        """
        Slot documentation goes here.

        @param checked DESCRIPTION
        @type bool
        """
        
        if checked:
            
            try:
                if self.cbKeypadPort.currentIndex() == 0:
                    self.keypadPort = self.GetKeypadPortObject()
                else:
                    self.keypadPort = self.GetUserPortObject()
                    
                self.keypadModbus = CModbus(self.keypadPort)
                self.keypadModbus.SetDeviceAddr(self.sbKpNodeAddr.value())
                self.keypad = CGDKeypad(self.keypadDisplay, self.keypadModbus, self.leParamDBFile.text())
                
                self.keypadTimer = QTimer()
                self.keypadTimer.timeout.connect(self.on_Keypad_Timeout)
                self.keypadTimer.start(100)
                self.keypad.Start()
                
                self.keypadPort.close()
                self.keypadPort.open()

            except Exception as e:
                QMessageBox.critical(self,"Faild to open port", str(e))
                self.btKpConnect.setChecked(False)
                return

            for widget in self.keypadWidgetList:
                widget.setEnabled(True)
                
            self.cbKeypadPort.setEnabled(False)
                
            self.keypadPort.flushInput()
                
               
        else:
            
            for widget in self.keypadWidgetList:
                widget.setEnabled(False)       
                
            self.cbKeypadPort.setEnabled(True)

                
            try:
                self.keypadPort.close()
                self.keypadTimer.stop()
                
                del self.keypadPort
                del self.keypadModbus
                del self.keypadTimer
                del self.keypad
            except Exception as e:
                self.btKpConnect.setChecked(False)
                QMessageBox.critical(self,"Faild to open port", str(e))
            
                
    @pyqtSlot()
    def on_btKpRun_clicked(self):
        """
        Slot documentation goes here.
        """
        self.keypad.RunAction(0)

    @pyqtSlot()
    def on_btKpDown_clicked(self):
        """
        Slot documentation goes here.
        """
        self.keypad.DownAction()


    @pyqtSlot(str)
    def on_cbScpName_currentIndexChanged(self, p0):
        """
        Slot documentation goes here.

        @param p0 DESCRIPTION
        @type str
        """
        try:
            addr = p0.split(' ')[1]
            self.leScpAddr.setText(addr)
        except:
            None

    @pyqtSlot()
    def on_actionAbout_triggered(self):
        """
        Slot documentation goes here.
        """
        about = CAbout(parent = self)
        about.show()

    @pyqtSlot()
    def on_btScpApply_clicked(self):
        """
        Slot documentation goes here.
        """
        
        ch = self.bgChGroup.checkedId()

        name = self.cbScpName.currentText()
        slaveAddr = self.sbScpSlaveAddr.value()

        if name == '':
            name = 'ch' + str(ch + 1)
            chName = name

        else:
            chName = name.split(' ')[0]

        self.bgChGroup.checkedButton().setText(chName)

        addr = self.leScpAddr.text()
        if addr != '':
            if len(addr) > 2 and (addr[0:2] == '0x' or addr[0:2] == '0X'):
                addr = int(addr, 16)
            else:
                try:
                    addr = int(addr)
                except:
                    addr = 0
        else:
            addr = 0

        try:
            scale = self.leScpScale.text()
            scale = float(scale)
        except:
            scale = 1.0

        try:
            offset = self.leScpOffset.text()
            offset = float(offset)
        except:
            offset = 0


        size = self.cbScpSize.currentIndex()
        type = self.cbScpSign.currentText()        
        isParam = self.cbScpIsParam.isChecked()
        active = self.cbScpActive.isChecked()

        self.scopeChannelList[ch].name = chName
        self.scopeChannelList[ch].slaveAddr = slaveAddr
        self.scopeChannelList[ch].addr = addr
        self.scopeChannelList[ch].size = size
        self.scopeChannelList[ch].type = type
        self.scopeChannelList[ch].scale = scale
        self.scopeChannelList[ch].offset = offset
        self.scopeChannelList[ch].isParam = isParam
        self.scopeChannelList[ch].active = active and (not addr == 0)

    
    @pyqtSlot(int)
    def on_sbKpSlaveAddr_valueChanged(self, p0):
        """
        Slot documentation goes here.
        
        @param p0 DESCRIPTION
        @type int
        """
        try:
            self.keypadModbus.setDeviceAddress(p0)
        except:
            pass
    
    @pyqtSlot()
    def on_btKpRun_pressed(self):
        """
        Slot documentation goes here.
        """
        self.keypad.RunPressed()
    
    @pyqtSlot()
    def on_btKpRun_released(self):
        """
        Slot documentation goes here.
        """
        self.keypad.RunReleased()
    
    @pyqtSlot()
    def on_btKpStop_pressed(self):
        """
        Slot documentation goes here.
        """
        self.keypad.StopPressed()
    
    @pyqtSlot()
    def on_btKpStop_released(self):
        """
        Slot documentation goes here.
        """
        self.keypad.StopReleased()
    
    @pyqtSlot()
    def on_btMDOpenFile_clicked(self):
        """
        Slot documentation goes here.
        """
        filePath, txt = QFileDialog.getSaveFileName(
            self,
            self.tr("Save mem file as"),
            "",
            self.tr(".txt"),
            self.tr(".txt"))
            
            
        self.leMDFilePath.setText(filePath)
        
    
    @pyqtSlot()
    def on_btMDDump_clicked(self):
        """
        Slot documentation goes here.
        """

        szCoutent = []

        count = int(self.spMDCount.text())

        szAddr = self.cbDCAddr.currentText()
        match = self.ptHex.search(szAddr)

        if match:
            addr = int(match.group()[2:],16)        
        else:
            self.leKpDisplay.setText('Dump Addr Err!')
            return
            
        sign = self.cbDCSign.currentText()        
        size = SIZE_TABLE[self.btDCBits.currentIndex()]
        
        try:            
            fout = open(self.leMDFilePath.text(), 'w')      
        except Exception as e:            
            QMessageBox.critical(self.tr("Error"), self.tr(str(e) + "\n Dump Failed!"))            
            
            return
            
        i = 0
        
        # This is target platform relevant, for C2000, the memory width is 16 bit per address
        if size == 'n8' or size == 'n16':
            addStep = 1
        else:
            addStep = 2
            
        while i < count:
            
            [err, value] = self.keypad.ReadAction(addr + i * addStep, False, size, sign)

            if err:
                s = '0x%08x Err:0x%02x\n'%(addr + i, err)
            else:
                s = '0x%08x %f\n'%(addr + i, value)
                
            szCoutent.append(s)
            
            i = i + 1
        
        fout.writelines(szCoutent)


        self.leKpDisplay.setText('Mem Dumped!')
    
    
    
    @pyqtSlot()

    def on_btDownloadFW_clicked(self):
        """
        Slot documentation goes here.
        """
        
        if self.cbRemoteFile.currentText() == '':
            QMessageBox.critical(self,"Error", "Local File unspecified!")
            return
        
        try:
            
            err, handle = self.DCMP.OpenFile('w', 'l', self.cbRemoteFile.currentText())
            
            if err != 0:
                QMessageBox.critical(self,"Error", "Transmition Error: 0x%02x"%err)
                return
                
            if handle <= 0:
                QMessageBox.critical(self,"Error", self.FAS[-handle])
                return                
            
            fileName = self.leLocalFile.text()
            with open(fileName, 'rb') as f:
                data = f.read()
                f.close()
                
            self.pbFileLoad.value = 0
                
            index = 0
            blockSize = self.DCMP.deviceBufferSize
            
            while index < len(data):
                
                if len(data) - index > blockSize:
                    count = blockSize
                else:
                    count = len(data) - index
                
                err, status = self.DCMP.WriteFile(handle, data[index:index+count])
                
                if err != 0:
                    QMessageBox.critical(self,"Error", "Transmition Error: 0x%02x"%err)
                    dcmp.CloseFile(handle, 0xFFFFFFFF, index)
                    return
                    
                if status < -1:
                    QMessageBox.critical(self,"Error", "Error %s at %d"%(self.FAS[-status], index))
                    dcmp.CloseFile(handle, 0xFFFFFFFF, index)
                    return
              
                elif status == 0:
                    QMessageBox.critical(self,"Error", "File tooo long!")
                    dcmp.CloseFile(handle, 0xFFFFFFFF, index)
                    return
                    
                index = index + status
                
                self.pbFileLoad.setValue(index * 100 / len(data))
                
                QApplication.processEvents() 
                    
                    
            crc = Crc32.Calc(data)
            
            err, status = self.DCMP.CloseFile(handle, len(data), crc)
            
            if status < -1:
                QMessageBox.critical(self,"Error", "Error %s when closing file"%(self.FAS[-status]))
                return
            else:
                self.AppendDCMPMsg('File Downloaded Successfully!')
                        
        except Exception as e:
            QMessageBox.critical(self,"Error", str(e))


    def AppendDCMPMsg(self, s):
        self.tbDCMPInfo.append('%s>: %s'%(time.strftime("%X", time.localtime()),  s))
    
    
    @pyqtSlot()
    def on_btUploadFW_clicked(self):
        """
        Slot documentation goes here.
        """
        QMessageBox.critical(self,"Error", 'Not Implemented')
    
    @pyqtSlot()
    def on_btSelectFW_clicked(self):
        """
        Slot documentation goes here.
        """
        localFile, txt = QFileDialog.getOpenFileName(
            self,
            self.tr("Select Local File"),
            "",
            self.tr("*.bin"),
            self.tr("*.bin"))
        
        self.leLocalFile.setText(localFile)
    
    @pyqtSlot()
    def on_btReset_clicked(self):
        """
        Slot documentation goes here.
        """
        
        resetType = self.cbResetType.currentIndex()
        
        try:            
            self.DCMP.Reset(resetType)            
        except Exception as e:
            QMessageBox.critical(self,"Error", str(e))
    
    @pyqtSlot(int)
    def on_spNodeAddress_valueChanged(self, p0):
        """
        Slot documentation goes here.
        
        @param p0 DESCRIPTION
        @type int
        """
        try:
            self.DCModBus.setDeviceAddress(p0)
        except:
            pass
    
    @pyqtSlot()
    def on_btDCWrite_clicked(self):
        """
        Slot documentation goes here.
        """
        szAddr = self.cbDCAddr.currentText()
        size = self.cbMemSize.currentIndex()            
        type = self.cbMemType.currentText()
        

        isParam = False

        try:
                
            if szAddr[0] == 'F':
                addr = szAddr[1:].split('.')
                isParam = True
                menu = addr[0]
                param = addr[1]
                
                if type == 'Float':
                    QMessageBox.critical( self, self.tr("Error"),'Float parameter access not supported')
                    return
                
            elif szAddr[0:2] == '0x' or szAddr[0:2] == '0X':
                addr = int(szAddr, 16)
            else:
                addr = int(szAddr)
                
        except:
            QMessageBox.critical( self, self.tr("Error"),'Invalid Address')
            return
            
                
        try:
            szValue = self.leMemValue.text()                
            if szValue[0:2] == '0x' or szValue[0:2] == '0X':
                value = int(szValue, 16)
            elif type == 'Float'and not isParam:
                value = float(szValue)
            else:
                value = int(szValue)
                
        except:
            QMessageBox.critical( self, self.tr("Error"),'Invalid Value')
            return
        
        valueList = [value]
        
        try:
            
            if isParam:
                err,  count = self.DCModBus.WriteSingle((int(menu) << 8) + int(param), value,  size == 2)
                
            else:                
                err,  count = self.DCMP.WriteMemory(size, type, addr, valueList)
            
            if err != 0:                
                self.AppendDCMPMsg('Failed to Write %s, Code: 0x%02x'%(szAddr, err))
            else:                 
                self.AppendDCMPMsg('Write OK')
                
        except Exception as e:
            QMessageBox.critical( self, self.tr("Error"),str(e))
        
    
    @pyqtSlot()
    def on_btDCRead_clicked(self):
        """
        Slot documentation goes here.
        """
        szAddr = self.cbDCAddr.currentText()
        size = self.cbMemSize.currentIndex()            
        type = self.cbMemType.currentText()
        

        isParam = False

        try:
                
            if szAddr[0] == 'F':
                addr = szAddr[1:].split('.')
                isParam = True
                menu = addr[0]
                param = addr[1]
                
                if type == 'Float':
                    QMessageBox.critical( self, self.tr("Error"),'Float parameter access not supported')
                    return
                
            elif szAddr[0:2] == '0x' or szAddr[0:2] == '0X':
                addr = int(szAddr, 16)
            else:
                addr = int(szAddr)
                
        except:
            QMessageBox.critical( self, self.tr("Error"),'Invalid Address')
            return
            
                
        
        try:
            
            if isParam:
                err,  valueList = self.DCModBus.Read((int(menu) << 8) + int(param), 1,  size == 2)
                value = valueList[0]
                
                if type == 'Int':
                    
                    if size == 0 and (value & 0x80) != 0:
                        value = value - 256
                    elif size == 1 and (value & 0x8000) != 0:
                        value = value - 65536
                    elif size == 2 and (value & 0x80000000) != 0:
                        value = 0x100000000
                    
                
            else:                
                err,  valueList = self.DCMP.ReadMemory(size, type, 1, addr)
                value = valueList[0]
            
            if err != 0:                
                self.AppendDCMPMsg('Failed to read %s, Code: 0x%02x'%(szAddr, err))
            else:                 
                self.leMemValue.setText(str(value))
                self.AppendDCMPMsg('Read OK')
                
        except Exception as e:
            QMessageBox.critical( self, self.tr("Error"),str(e))

    
    @pyqtSlot(bool)
    def on_btDCConnect_toggled(self, checked):
        """
        Slot documentation goes here.
        
        @param checked DESCRIPTION
        @type bool
        """
        
        if checked:
            
            try:     
                
                if self.cbDCPort.currentIndex() == 0:
                    self.DCPort = self.GetKeypadPortObject()
                else:
                    self.DCPort = self.GetUserPortObject()
                self.DCModBus = CModbus(self.DCPort)
                self.DCModBus.SetDeviceAddr(self.spNodeAddress.value())
                self.DCMP = CDCMP(self.DCModBus)
            

                self.DCPort.close()
                self.DCPort.open()
                self.DCPort.flushInput()
            except Exception as e:
                QMessageBox.critical(self,"Faild to open port", str(e))
                self.btDCConnect.setChecked(False)
                return

            for widget in self.deviceCtlWidgetList:
                widget.setEnabled(True)
                
            self.cbDCPort.setEnabled(False)
                
            try:
                self.DCMP.EnableDCMP()
                
                request  = [i for i in range(0, CDCMP.DI_COUNT)]
                err,  info = self.DCMP.RequestDeviceInfo(request)
            
                if err == 0:
                    for key in info.keys():
                        self.tbDCMPInfo.append('%s: %s'%(key, str(info[key])))
                else:
                    self.tbDCMPInfo.append('Error: 0x%02x\n'%err)
                    
            except Exception as e:
                QMessageBox.critical(self,"Error", str(e))
                
                
        else:
            
            for widget in self.deviceCtlWidgetList:
                widget.setEnabled(False)
                
            self.cbDCPort.setEnabled(True)
            
            try:
                self.DCPort.close()
                
                del self.DCPort
                del self.DCModBus
                del self.DCMP
                
            except:
                pass
            

    
    @pyqtSlot()
    def on_btUpdatePortList_clicked(self):
        """
        Slot documentation goes here.
        """
        self.UpdateCommPortList()
    
    def ParamDump(self, NonDefaultOnly):
        
        paramFileName = self.lePDFilePath.text()
        
        if paramFileName == '':
            QMessageBox.critical(self,"Error", 'File unspecified')
            return

        hFile = open(paramFileName, 'w')    
        
        keypad = CGDKeypad(None, self.DCModBus)
        
        keypad.DumpParam(hFile, NonDefaultOnly)
        
        hFile.close()
        
        del keypad
        
        self.AppendDCMPMsg('Parameters Dumped successfully!')
    
    @pyqtSlot()
    def on_btPDDumpND_clicked(self):
        """
        Slot documentation goes here.
        """
        self.ParamDump(True)
        
    @pyqtSlot()
    def on_btPDLoad_clicked(self):
        """
        Slot documentation goes here.
        """
        
        paramFileName = self.lePDFilePath.text()
        
        if paramFileName == '':
            QMessageBox.critical(self,"Error", 'File unspecified')
            return
        
        with open(paramFileName) as f:
            for line in f.readlines():
                info = line.split(',')
                menu = int(info[0])
                param = int(info[1])
                format = int(info[2], 16)
                value = int(info[3])

                rw = format & 0x03
                # Only write parameter that is not RO
                if rw != 2:
                        
                    size = ((format >> 3) & 0x0f)
                    if size == 3:
                        size = 'n16'
                    elif size == 4:
                        size = 'n32'
                    else:
                        size = 'n8'
    
                    self.DCModBus.WriteSingle((menu << 8) + param, value, size == 'n32', False, True)
            
        self.statusBar.showMessage('Parameters Loaded successfully!')
    
    @pyqtSlot()
    def on_btPDDumpAll_clicked(self):
        """
        Slot documentation goes here.
        """
        self.ParamDump(False)

    
    @pyqtSlot()
    def on_btPDFileOpen_clicked(self):
        """
        Slot documentation goes here.
        """
        paramFileName, filter = QFileDialog.getSaveFileName(
            self,
            self.tr("Save Param File"),
            "",
            self.tr("*.txt"),
            None)
            
        self.lePDFilePath.setText(paramFileName)
    
    @pyqtSlot()
    def on_btClrDCMPInfo_clicked(self):
        """
        Slot documentation goes here.
        """
        self.tbDCMPInfo.clear()

    
    @pyqtSlot()
    def on_btOpenParamDB_clicked(self):
        """
        Slot documentation goes here.
        """
        dbFile, txt = QFileDialog.getOpenFileName(
            self,
            self.tr("Select Parameter Database File"),
            "",
            self.tr("*.db"),
            self.tr("*.db"))
            
        self.leParamDBFile.setText(dbFile)
    
    @pyqtSlot()
    def on_rbScopeSlow_clicked(self):
        """
        Slot documentation goes here.
        """
        self.cbScpIsParam.setChecked(True)
    
    @pyqtSlot()
    def on_rbScopeFast_clicked(self):
        """
        Slot documentation goes here.
        """
        self.cbScpIsParam.setChecked(False)
    
    def SetKeypadMode(self, mode):
        """
        Slot documentation goes here.
        """
        if self.btKpConnect.isChecked() and self.cbKeypadPort.currentIndex() == 0:
            QMessageBox.critical( self, self.tr("Error"), 'Stop keypad first')
            return
            
        try:
            err,  status = self.DCMP.SetKeypadMode(mode)
            
            if err == 0:
                
                if status == 0:
                    self.AppendDCMPMsg('Keypad port Changed successfully')
                else:
                    self.AppendDCMPMsg('Changed keypad mode not supported')
                    
                
            else:
                self.AppendDCMPMsg('Comm Failed, Error code: 0x%02x'%err)
                
        except Exception as e:
            QMessageBox.critical( self, self.tr("Error"), str(e))
    
    @pyqtSlot()
    def on_actionSetToKeypad_triggered(self):
        """
        Slot documentation goes here.
        """
        self.SetKeypadMode(0)
    
    @pyqtSlot()
    def on_actionSetToScope_triggered(self):
        """
        Slot documentation goes here.
        """
        self.SetKeypadMode(1)
